#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "scanner.h"
#include "xmalloc.h"
#include "tag.h"
#include "var.h"

static size_t tags_found = 0;
static size_t attrs_found = 0;
char doc_dir[MAXPATHLEN+1];
pdata_arr_t *pdata = NULL;

const char *
build_tag_str(const char *__tagName, const tagType_t __tagType, string_t *__str)
{
if (!__str) return (NULL);
  switch (__tagType)
  {
    case START_ELEM:
      set_nstring(__str, "<", 1);
      break;
    case START_TAG:
      build_tag_str(NULL, START_ELEM, __str);
      add_string(__str, __tagName);
      break;

    case END_ELEM:
    case END_TAG:
      set_nstring(__str, ">", 1);

    default:
      break;
  }
  return (get_string(__str));
}

void
remove_tag(string_t *__html, char *__start_ptr, size_t __len)
{
  char *start = NULL;
  size_t pos = 0;

  start = (char *)get_string(__html);
  pos = (__start_ptr - start);
  del_string(__html, pos, __len + 1);
}

static pdata_arr_t *
parse_data_create(string_t *__filename, string_t *__html, tag_arr_t *__tags, pdata_arr_t *__parsedata)
{
  pdata_arr_t *arr = __parsedata;
  parse_data_t pd;

  if (!arr)
  {
    arr = (pdata_arr_t *) array_create(sizeof(struct parse_data), 4);
  }
  pd.file_name = create_dupstring(__filename);
  pd.html = create_dupstring(__html);
  pd.tags = __tags;
  array_add_data(arr, (void *)&pd, 1);
  return (arr);
}

static tag_arr_t *
tag_data_create(size_t __index, dict_t *__attrs, tag_arr_t *__tags)
{
  tag_arr_t *tags = __tags;
  tag_data_t td;

  if (!tags)
  {
    tags = (tag_arr_t *) array_create(sizeof(struct tag_data), 2);
  }
  td.idx = __index;
  td.var_ptr = NULL;
  td.attrs = __attrs;
  array_add_data(tags, (void *)&td, 1);
  return (tags);
}

pdata_arr_t *
parse_html(const char *tag_name, const dirlist_elem_t *elem, pdata_arr_t *pdata_ptr)
{
  char *tag_offset = NULL, *attr_offset, *start_tag;
  size_t tag_len = 0, tag_idx, fattrs_found = 0, tattrs_found = 0, ttags_found = 0;
  int tretval = -1, aretval = -1;
  string_t *tag = (string_t *) create_empty_string();
  dict_t *dst = NULL;
  pdata_arr_t *pd = pdata_ptr;
  tag_arr_t *tags = NULL;

  I("parsing file %s", get_string(elem->path));
  tag_offset = (char *)get_string(elem->data);
  while ( (tretval = scan_tag(tag_name, &tag_offset, &start_tag, &tag_len, tag, SCANFLG_DEFAULT)) != SCANERR_NOTFOUND )
  {
    if (tretval == SCANERR_OK || tretval == SCANERR_SIMPLETAG)
    {
      tattrs_found = 0;
      ttags_found++;
      attr_offset = (char *)get_string(tag);
      dst = (dict_t *) create_dict(true);
      while ( (aretval = scan_tag_attr(&attr_offset, dst)) != SCANERR_NOTFOUND )
      {
        if( aretval == SCANERR_OK )
        {
          attrs_found++;
          tattrs_found++;
          fattrs_found++;
        } else if ( aretval == SCANERR_NOVALID )
        {
          E("%s: invalid tag attribute at pos %lu", get_string(elem->path), (off_t)(tag_offset - get_string(elem->data)));
          print_str_cutout(elem->data, tag_offset, CUTOUT_WIDTH);
        }
      }
      tags_found++;
      if (tretval == SCANERR_SIMPLETAG) {
        attrs_found--;
        tattrs_found--;
        fattrs_found--;
      }
      DD("%s: TAG%lu: found %lu attribute(s)", get_string(elem->path), (long unsigned int)tags_found, (long unsigned int)tattrs_found);
      remove_tag(elem->data, start_tag, tag_len);
      tag_offset = (tag_offset - tag_len);
      tag_idx = (size_t)(start_tag - get_string(elem->data));
      tags = tag_data_create(tag_idx, dst, tags);
      dst = NULL;

    } else if ( tretval == SCANERR_NOVALID )
    {
      E("%s: invalid tag: %s at pos %lu", get_string(elem->path), tag_name, (off_t)(tag_offset - get_string(elem->data)));
      print_str_cutout(elem->data, tag_offset, CUTOUT_WIDTH);
    }
  }
  I("%s: found %lu tag(s) with %lu attribute(s)", get_string(elem->path), (long unsigned int)ttags_found, (long unsigned int)fattrs_found);
  free_string(tag);
  return ( parse_data_create(elem->path, elem->data, tags, pd) );
}

void
free_all_parse_data(pdata_arr_t *pd_ptr)
{
  size_t idx, idx2;
  parse_data_t *pd = NULL;
  tag_data_t *td = NULL;
  tag_arr_t *tags = NULL;

  if (pd_ptr)
  {
    for (idx = 0; idx < pd_ptr->used; idx++)
    {
      pd = (parse_data_t *) array_get_offset(pd_ptr, idx);
      if (!pd) continue;
      if (pd->tags)
      {
        tags = pd->tags;
        for (idx2 = 0; idx2 < tags->used; idx2++)
        {
          td = (tag_data_t *) array_get_offset(tags, idx2);
          free_dict(td->attrs);
        }
      }

      free_string(pd->html);
      free_string(pd->file_name);
      array_free(pd->tags);
      pd = NULL;
    }
    array_free(pd_ptr);
  }
}

bool
init_html(const char *__search_tag)
{
  size_t idx = 0, si = 0, files = 0;
  dirlist_t *html_files;
  dirlist_elem_t *html_file;

  I("parsing files in %s with html tag: %s", doc_dir, __search_tag);
  html_files = (dirlist_t *)listfiles(doc_dir, NC_FILE_SUFFIX, NULL);
  while ( (html_file = listfiles_foreach(html_files, &idx)) != NULL)
  {
    if ( !mmap_data(html_file) ) {
      W("can not map file %s into buffer", get_string(html_file->path));
      continue;
    }
    pdata = parse_html(__search_tag, html_file, pdata);
    files++;
    if ( get_strlen(html_file->path) > (si = strnlen(doc_dir, MAXPATHLEN)) ) {
      url_register(get_string(html_file->path)+si, get_string(html_file->path), URL_BUILTIN, (void *) array_last(pdata));
    }
  }
  I("found %lu tag(s) with %lu attribute(s) in %lu files", (long unsigned int)tags_found, (long unsigned int)attrs_found, (long unsigned int)files); 
  if (!pdata || !html_files)
  {
    E("no juicy data found in: %s", doc_dir);
    return false;
  }
  listfiles_cleanup(html_files);

  return (true);
}

